package classes;

import java.awt.image.BufferedImage;

import qrlib.Decoder;
import qrlib.DecoderResult;

/**
 * QRCode.java
 * 
 * @author Nicole Nelson
 */
public class QRCode 
{
	private boolean[][] dataArray;
	private BufferedImage code;
	private int BLACK = -16777216;
	private String url;
	private int[] versions; 
	public boolean display;
	
	public QRCode()
	{
		versions = new int[11];
		versions[0] = 0;
		versions[1] = 21;
		versions[2] = 25;
		versions[3] = 29;
		versions[4] = 33;
		versions[5] = 37;
		versions[6] = 41;
		versions[7] = 45;
		versions[8] = 49;
		versions[9] = 53;
		versions[10] = 57;
	}
	
	/*
	 * Sets the size of the dataArray to the specified size
	 */
	private void setDataArraySize(int s)
	{
		int loop = versions.length - 1;
		while(s < versions[loop])
			loop--;
		dataArray = new boolean[versions[loop]][versions[loop]];
	}
	
	/*
	 * Sets the code image
	 */
	public void setCode(BufferedImage c)
	{
		code = c;
	}
	
	/*
	 * Returns the code image
	 */
	public BufferedImage getCode()
	{
		return code;
	}
	
	/*
	 * Figures out what the boolean array of the qr code is
	 * And then decodes the code using the boolean array and the qr image library
	 */
	public void DecodeCode()
	{
		int blocksize = 0;
		int loop1 = 0;		
		
		int imgHeight = code.getHeight();
		int imgWidth = code.getWidth();
		
		int temp = 0;
		
		//Gets the average size of the blocks based on the size of the alignment patterns
		while(loop1 < imgHeight && loop1 < imgWidth && code.getRGB(loop1, loop1) != BLACK)
			loop1++;
		temp = loop1;
		while(loop1 < imgHeight && loop1 < imgWidth && code.getRGB(loop1, loop1) == BLACK)
			loop1++;
		while(loop1 < imgHeight && loop1 < imgWidth && code.getRGB(loop1, loop1) != BLACK)
			loop1++;
		while(loop1 < imgHeight && loop1 < imgWidth && code.getRGB(loop1, loop1) == BLACK)
			loop1++;
		while(loop1 < imgHeight && loop1 < imgWidth && code.getRGB(loop1, loop1) != BLACK)
			loop1++;
		while(loop1 < imgHeight && loop1 < imgWidth && code.getRGB(loop1, loop1) == BLACK)
			loop1++;
		loop1--;
		
		blocksize = Math.round((float)loop1 / 7);
		
		//Calculates the total number of blocks of data
		float actNumH = (float)(imgHeight) / blocksize;
		int roundedNumH = Math.round(actNumH);
		float actNumW = (float)(imgWidth) / blocksize;
		int roundedNumW = Math.round(actNumW);
		int roundedNum  = Math.max(roundedNumW, roundedNumH);
		setDataArraySize(roundedNum);

		int height = blocksize / 2 - 1 + temp;
		int indexX = 0;
		int indexY = 0;
		int deltaHeight = Math.round((float)(imgHeight - temp) / dataArray.length);
		
		//Finds each block of data
		while(height < imgHeight && height > 0)
		{
			int current = 0;
			int total = 0;
			int blocks = 0;
			indexY = 0;
			
			//Makes sure the point is in the middle area of the block if not it adjusts it
			for (int i = 1; i < blocksize / 2; i++)
			{
				if(height - i > 0 && height + i < imgHeight && code.getRGB(blocksize / 2 - 1 + temp, height - i) != code.getRGB(blocksize / 2 - 1 + temp, height + i))
				{
					if(code.getRGB(blocksize / 2 - 1 + temp, height - i) == code.getRGB(blocksize / 2 - 1 + temp, height))
						height -= i;
					else
						height += i;
					break;
				}
			}
			current = code.getRGB(0, height);
			for (int width = 0; width < imgWidth; width++)
			{
				if (code.getRGB(width, height) != current)
				{
					total--;
					blocks = Math.round((float)total / blocksize);
					for (int k = 0; k < blocks; k++)
					{
						if (indexX < dataArray.length && indexY < dataArray[indexX].length)
						{
							if(current == BLACK)
								dataArray[indexX][indexY] = true;
							else
								dataArray[indexX][indexY] = false;
							indexY++;
						}	
					}
					current = code.getRGB(width, height);
					if(blocks != 0)
						total = 0;
				}
				else
					total++;
			}
			
			if (indexX < dataArray.length && indexY < dataArray[indexX].length && total > 0)
			{
				blocks = Math.round((float)total / blocksize);
				for (int k = 0; k < blocks; k++)
				{
					if (indexY < dataArray[indexX].length)
					{
						if(current == BLACK)
							dataArray[indexX][indexY] = true;
						else
							dataArray[indexX][indexY] = false;
						indexY++;
					}	
				}
			}
			height += deltaHeight;
			indexX++;
		}
		
		//Displays the decode array to the console screen
		if(display)
			for (int row = 0; row < dataArray.length; row++)
			{
				for (int col = 0; col < dataArray[row].length; col++)
				{
					if(dataArray[row][col])
						System.out.print("1 ");
					else
						System.out.print("0 ");
				}
				System.out.print("\n");
			}	
		
		//Calls the library to decode the boolean array
		Decoder decode = new Decoder();
		DecoderResult result = null;
		try
		{
			result = decode.decode(dataArray);
			url = result.getText();
		}
		catch(Exception e)
		{
			url = "ERROR: Unable to decode QR Code";
		}
		
		//Displays the results to the Console screen
		System.out.println(url);
		
	}
	
	/*
	 * returns the resulting url of the QRCode
	 * (This can also be just plain text or anything that can be converted to a
	 * string but because of the scope of this project it should always be a url).
	 */
	public String getURL()
	{
		return url;
	}
}
